﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;
using NetTopologySuite.GeometriesGraph;
using NetTopologySuite.Noding;

namespace NetTopologySuite.Operation.Buffer
{
	public class OffsetCurveSetBuilder
	{
		public OffsetCurveSetBuilder(IGeometry inputGeom, double distance, OffsetCurveBuilder curveBuilder)
		{
			this._inputGeom = inputGeom;
			this._distance = distance;
			this._curveBuilder = curveBuilder;
		}

		public IList<ISegmentString> GetCurves()
		{
			this.Add(this._inputGeom);
			return this._curveList;
		}

		private void AddCurve(Coordinate[] coord, Location leftLoc, Location rightLoc)
		{
			if (coord == null || coord.Length < 2)
			{
				return;
			}
			NodedSegmentString item = new NodedSegmentString(coord, new Label(0, Location.Boundary, leftLoc, rightLoc));
			this._curveList.Add(item);
		}

		private void Add(IGeometry g)
		{
			if (g.IsEmpty)
			{
				return;
			}
			if (g is IPolygon)
			{
				this.AddPolygon((IPolygon)g);
				return;
			}
			if (g is ILineString)
			{
				this.AddLineString(g);
				return;
			}
			if (g is IPoint)
			{
				this.AddPoint(g);
				return;
			}
			if (g is IMultiPoint)
			{
				this.AddCollection(g);
				return;
			}
			if (g is IMultiLineString)
			{
				this.AddCollection(g);
				return;
			}
			if (g is IMultiPolygon)
			{
				this.AddCollection(g);
				return;
			}
			if (g is IGeometryCollection)
			{
				this.AddCollection(g);
				return;
			}
			throw new NotSupportedException(g.GetType().FullName);
		}

		private void AddCollection(IGeometry gc)
		{
			for (int i = 0; i < gc.NumGeometries; i++)
			{
				IGeometry geometryN = gc.GetGeometryN(i);
				this.Add(geometryN);
			}
		}

		private void AddPoint(IGeometry p)
		{
			if (this._distance <= 0.0)
			{
				return;
			}
			Coordinate[] coordinates = p.Coordinates;
			Coordinate[] lineCurve = this._curveBuilder.GetLineCurve(coordinates, this._distance);
			this.AddCurve(lineCurve, Location.Exterior, Location.Interior);
		}

		private void AddLineString(IGeometry line)
		{
			if (this._distance <= 0.0 && !this._curveBuilder.BufferParameters.IsSingleSided)
			{
				return;
			}
			Coordinate[] inputPts = CoordinateArrays.RemoveRepeatedPoints(line.Coordinates);
			Coordinate[] lineCurve = this._curveBuilder.GetLineCurve(inputPts, this._distance);
			this.AddCurve(lineCurve, Location.Exterior, Location.Interior);
		}

		private void AddPolygon(IPolygon p)
		{
			double offsetDistance = this._distance;
			Positions positions = Positions.Left;
			if (this._distance < 0.0)
			{
				offsetDistance = -this._distance;
				positions = Positions.Right;
			}
			Coordinate[] array = CoordinateArrays.RemoveRepeatedPoints(p.Shell.Coordinates);
			if (this._distance < 0.0 && this.IsErodedCompletely(array, this._distance))
			{
				return;
			}
			if (this._distance <= 0.0 && array.Length < 3)
			{
				return;
			}
			this.AddPolygonRing(array, offsetDistance, positions, Location.Exterior, Location.Interior);
			for (int i = 0; i < p.NumInteriorRings; i++)
			{
				Coordinate[] array2 = CoordinateArrays.RemoveRepeatedPoints(((ILinearRing)p.GetInteriorRingN(i)).Coordinates);
				if (this._distance <= 0.0 || !this.IsErodedCompletely(array2, -this._distance))
				{
					this.AddPolygonRing(array2, offsetDistance, Position.Opposite(positions), Location.Interior, Location.Exterior);
				}
			}
		}

		private void AddPolygonRing(Coordinate[] coord, double offsetDistance, Positions side, Location cwLeftLoc, Location cwRightLoc)
		{
			if (offsetDistance == 0.0 && coord.Length < 4)
			{
				return;
			}
			Location leftLoc = cwLeftLoc;
			Location rightLoc = cwRightLoc;
			if (coord.Length >= 4 && CGAlgorithms.IsCCW(coord))
			{
				leftLoc = cwRightLoc;
				rightLoc = cwLeftLoc;
				side = Position.Opposite(side);
			}
			Coordinate[] ringCurve = this._curveBuilder.GetRingCurve(coord, side, offsetDistance);
			this.AddCurve(ringCurve, leftLoc, rightLoc);
		}

		private bool IsErodedCompletely(Coordinate[] ringCoord, double bufferDistance)
		{
			if (ringCoord.Length < 4)
			{
				return bufferDistance < 0.0;
			}
			if (ringCoord.Length == 4)
			{
				return OffsetCurveSetBuilder.IsTriangleErodedCompletely(ringCoord, bufferDistance);
			}
			return new MinimumDiameter(this._inputGeom.Factory.CreateLinearRing(ringCoord)).Length < 2.0 * System.Math.Abs(bufferDistance);
		}

		private static bool IsTriangleErodedCompletely(Coordinate[] triangleCoord, double bufferDistance)
		{
			Triangle triangle = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
			return CGAlgorithms.DistancePointLine(triangle.InCentre(), triangle.P0, triangle.P1) < System.Math.Abs(bufferDistance);
		}

		private readonly IGeometry _inputGeom;

		private readonly double _distance;

		private readonly OffsetCurveBuilder _curveBuilder;

		private readonly IList<ISegmentString> _curveList = new List<ISegmentString>();
	}
}
